package com.base.service.system.service.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.base.core.context.utils.JwtWrapper;
import com.base.core.framework.sql.service.impl.SqlBaseServiceStreamImplV1Ext;
import com.base.core.head.constants.CodeResVal;
import com.base.service.base.logger.entity.LoginLogDO;
import com.base.service.base.logger.service.LoginLogService;
import com.base.service.system.ao.UserAO;
import com.base.service.system.ao.UserLoginAO;
import com.base.service.system.ao.UserPwdAO;
import com.base.service.system.dao.UserDao;
import com.base.service.system.dto.JwtUserDTO;
import com.base.service.system.entity.RoleDO;
import com.base.service.system.entity.UserDO;
import com.base.service.system.service.AuthService;
import com.base.service.system.service.RoleService;
import com.base.service.system.service.UserService;
import com.base.service.system.utils.AuthListUtils;
import com.base.service.system.vo.AuthVO;
import com.base.service.system.vo.RoleVO;
import com.base.service.system.vo.UserLoginVO;
import com.base.service.system.vo.UserVO;
import com.gitee.magic.core.utils.GoogleAuthenticator;
import com.gitee.magic.framework.base.constant.Config;
import com.gitee.magic.framework.head.exception.BusinessException;
import com.gitee.magic.framework.head.utils.Assert;
import com.gitee.magic.jdbc.persistence.source.jdbc.sqlplus.conditions.update.UpdateWrapper;

import jakarta.annotation.Resource;

/**
 * @author start
 */
@Service("userService")
public class UserServiceImpl extends SqlBaseServiceStreamImplV1Ext<UserDO, UserVO> implements UserService {

	public static final String USER = "USER";

	private final UserDao userDao;

	@Value("${verify.mfa:false}")
	private Boolean verifyMfa;
	
	@Value("${jwt.token.secret:1234}")
	private String secret;
	
	@Resource
	private RoleService roleService;
	@Resource
	private AuthService authService;
	@Resource
	private LoginLogService loginLogService;

	public UserServiceImpl(@Qualifier("userDao") UserDao userDao) {
		super(userDao,UserVO.class);
		this.userDao = userDao;
	}

	@Override
	public UserLoginVO login(UserLoginAO ao, String requestIp) {
		UserDO user = load("userName",ao.getUserName());
		checkUserNotDisable(user);
		Assert.isFalse(ao.getPassword().equals(user.getPassword()), CodeResVal.CODE_10015);
		
		if(verifyMfa) {
			GoogleAuthenticator ga = new GoogleAuthenticator();
			ga.setWindowSize(1);
			if(!ga.checkCode(user.getSecretKey(),ao.getCode(), System.currentTimeMillis())) {
				throw new BusinessException(CodeResVal.CODE_10016);
			}
		}
		
		UserVO userVo=new UserVO();
		BeanUtils.copyProperties(user, userVo);
		userVo.setRoles(roleService.queryForByIds(RoleVO.class, user.getRoleIds()));

		LoginLogDO loginLog = loginLogService.buildLog(user.getId(), requestIp, "WEB", USER);
		JwtUserDTO jwtUser = new JwtUserDTO();
		jwtUser.setUser(userVo);
		jwtUser.setAccessId(String.valueOf(loginLog.getUserId()));
		
		JwtWrapper jwt = new JwtWrapper(secret);
		UserLoginVO uLoginVo = new UserLoginVO();
		uLoginVo.setUser(userVo);
		uLoginVo.setToken(jwt.getToken(jwtUser));
		
		return uLoginVo;
	}

	@Override
	public void fill(List<UserVO> list) {
		List<Long> roleIds=list
				.stream().map(UserVO::getRoleIds).collect(Collectors.toList())
				.stream().flatMap(List::stream).distinct().collect(Collectors.toList());
		Map<Long,RoleVO> roleMap=roleService.streamToMapVo(roleIds,Number::longValue);
		for (UserVO v : list) {
			List<RoleVO> rList=new ArrayList<>();
			for(Long roleId:v.getRoleIds()) {
				if(roleMap.containsKey(roleId)) {
					rList.add(roleMap.get(roleId));
				}
			}
			v.setRoles(rList);
		}
	}

	@Override
	public List<AuthVO> userAuthTree(Long parentId,Long userId) {
		List<AuthVO> allAuth = authService.tree(parentId);
		UserDO user=load(userId);
		List<RoleDO> roles=roleService.queryForByIds(user.getRoleIds());
		List<Long> authIds=roles
				.stream().map(RoleDO::getAuthIds).collect(Collectors.toList())
				.stream().flatMap(List::stream).distinct().collect(Collectors.toList());
		return AuthListUtils.build(allAuth, authIds);
	}

	@Override
	public void checkUserNameExist(String userName) {
		select1(null,"username", userName, CodeResVal.CODE_10019);
	}

	@Override
	public void save(UserAO ao) {
		UserDO user = new UserDO();
		BeanUtils.copyProperties(ao, user);
		if(user.getId()==null) {
			user.setSecretKey(GoogleAuthenticator.generateSecretKey());
		}
		save(user);
	}

	@Override
	public void setPassword(UserPwdAO ao,Long userId) {
		UpdateWrapper wrapper=new UpdateWrapper();
		wrapper.eq("id", userId).eq("password", ao.getOldPassword()).set("password", ao.getNewPassword());
		if(userDao.executeUpdate(wrapper)!=1) {
			throw new BusinessException(CodeResVal.CODE_10005);
		}
	}

	@Override
	public String restMfa(Long userId) {
		String secretKey=GoogleAuthenticator.generateSecretKey();
    	UpdateWrapper wrapper=new UpdateWrapper();
		wrapper.eq("id", userId)
		.set("secretKey", secretKey);
		if(userDao.executeUpdate(wrapper)!=1) {
			throw new BusinessException(CodeResVal.CODE_10005);
		}
    	return GoogleAuthenticator.getQrBarcode(Config.getSystemName()+"_"+Config.getActive(), secretKey);
	}

}
